package com.java.app.hacktouch.bluetooth

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.util.Log
import com.java.app.hacktouch.MainActivity
import com.java.app.hacktouch.bluetooth.BtDevice.BtState.STATE_LISTEN
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.*

class BtDevice(context: Context, private val mHandler: Handler) {

    private val lock = java.lang.Object()
    private val mAdapter: BluetoothAdapter
    private var mConnectThread: ConnectThread? = null
    private var mConnectedThread: ConnectedThread? = null
    private var mState: Int = 0

    var state: Int
        @Synchronized get() = mState
        @Synchronized private set(state) {
            Log.d(TAG, "setState() $mState -> $state")
            mState = state
            mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget()
        }

    init {
        mAdapter = BluetoothAdapter.getDefaultAdapter()

        mState = BtState.STATE_NONE
    }

    @Synchronized
    fun start() {
        Log.d(TAG, "start")

        if (mConnectThread != null) {
            mConnectThread!!.cancel()
            mConnectThread = null
        }

        if (mConnectedThread != null) {
            mConnectedThread!!.cancel()
            mConnectedThread = null
        }

        state = STATE_LISTEN
    }

    @Synchronized
    fun connect(device: BluetoothDevice) {
        Log.i(TAG, "connect to: " + device.name)

        if (mState == BtState.STATE_CONNECTING) {
            if (mConnectThread != null) {
                mConnectThread!!.cancel()
                mConnectThread = null
            }
        }

        if (mConnectedThread != null) {
            mConnectedThread!!.cancel()
            while (mConnectedThread!!.isAlive == true) {
                lock.wait(100)
            }
            mConnectedThread = null
        }

        mConnectThread = ConnectThread(device)
        mConnectThread!!.start()
        state = BtState.STATE_CONNECTING
    }

    @Synchronized
    fun connected(socket: BluetoothSocket, device: BluetoothDevice) {
        Log.d(TAG, "connected")

        if (mConnectThread != null) {
            mConnectThread!!.cancel()
            mConnectThread = null
        }

        if (mConnectedThread != null) {
            mConnectedThread!!.cancel()
            mConnectedThread = null
        }

        mConnectedThread = ConnectedThread(socket)
        mConnectedThread!!.start()

        state = BtState.STATE_CONNECTED
    }

    @Synchronized
    fun stop() {
        Log.d(TAG, "stop")

        if (mConnectedThread != null) {
            mConnectedThread!!.cancel()

            synchronized(lock) {
                if (mConnectedThread!!.isAlive) {
                    lock.wait(100)
                }
            }

            mConnectedThread = null
        }

        if (mConnectThread != null) {
            mConnectThread!!.cancel()

            synchronized(lock) {
                if (mConnectThread!!.isAlive) {
                    lock.wait(100)
                }
            }

            mConnectThread = null
        }

        state = BtState.STATE_NONE
    }

    fun write(out: IntArray) {
        var r: ConnectedThread?
        synchronized(this) {
            if (mState != BtState.STATE_CONNECTED)
                return
            r = mConnectedThread
            r!!.write(out)
        }
    }

    fun write(out: Int) {
        var r: ConnectedThread?
        synchronized(this) {
            if (mState != BtState.STATE_CONNECTED)
                return
            r = mConnectedThread
            r!!.write(out)
        }
    }

    private fun connectionFailed() {
        state = STATE_LISTEN

        /*
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(MainActivity.TOAST, "无法创建蓝牙数据连接，其他设备可能正在使用");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // Send a reconnect message back to the Activity
        Message msg_fail = mHandler.obtainMessage(MainActivity.CONNECT_FAIL);
        mHandler.sendMessage(msg_fail);
        */
    }

    /*
     * 连接线程
     * */
    private inner class ConnectThread(private val mmDevice: BluetoothDevice) : Thread() {
        private val mmSocket: BluetoothSocket?

        init {
            var tmp: BluetoothSocket? = null

            try {
                tmp = mmDevice.createRfcommSocketToServiceRecord(COM_UUID)
            } catch (e: IOException) {
                Log.e(TAG, "create() failed", e)
            }

            mmSocket = tmp
        }

        override fun run() {
            Log.i(TAG, "BEGIN mConnectThread")
            name = "ConnectThread"

            mAdapter.cancelDiscovery()

            try {
                mmSocket!!.connect()
            } catch (e: IOException) {
                Log.e(TAG, "unable to close() socket during connection failure", e)
                connectionFailed()
                try {
                    mmSocket!!.close()
                } catch (e2: IOException) {
                    Log.e(TAG, "unable to close() socket during connection failure", e2)
                }

                this@BtDevice.start()
                return
            }

            synchronized(this@BtDevice) {
                mConnectThread = null
            }

            connected(mmSocket, mmDevice)
        }

        fun cancel() {
            try {
                mmSocket!!.close()
            } catch (e: IOException) {
                Log.e(TAG, "close() of connect socket failed", e)
            }

        }
    }

    /*
     * 数据交互进程
     *
     * */
    private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
        private val mmInStream: InputStream?
        private val mmOutStream: OutputStream?
        private var isExit: Boolean = false

        init {
            Log.d(TAG, "create ConnectedThread")
            var tmpIn: InputStream? = null
            var tmpOut: OutputStream? = null

            try {
                tmpIn = mmSocket.inputStream
                tmpOut = mmSocket.outputStream
            } catch (e: IOException) {
                Log.e(TAG, "temp sockets not created", e)
            }

            mmInStream = tmpIn
            mmOutStream = tmpOut
            isExit = false
        }

        override fun run() {
            Log.i(TAG, "BEGIN mConnectedThread")
            val buffer = IntArray(BLE_DATA_LENGTH)
            var len = 0
            var bytes = 0
            var status = 0
            while (true) {

                try {

                    if (isExit == true) {
                        break
                    }

                    for (i in 0 until mmInStream!!.available()) {
                        when (status) {
                            0 -> if (mmInStream.read() == BtConstant.BE_SOF) {
                                status = 1
                            }
                            1 -> {
                                len = mmInStream.read()
                                status = 2
                            }
                            2 -> {
                                buffer[bytes] = mmInStream.read()
                                Log.d(TAG, "len: " + bytes + " val:" + buffer[bytes])
                                bytes++
                                if (bytes == len) {
                                    mHandler.obtainMessage(MainActivity.MESSAGE_READ, buffer[0], len, buffer)
                                            .sendToTarget()

                                    bytes = 0
                                    status = 0
                                }
                            }
                        }
                    }

                    try {
                        Thread.sleep(400)
                    } catch (e: InterruptedException) {
                        Log.e(TAG, "InterruptedException:" + e)
                    }

                } catch (e: IOException) {
                    Log.e(TAG, "bluetooth read IOException--> reset socket", e)
                    val msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST)
                    val bundle = Bundle()
                    bundle.putString(MainActivity.TOAST, "蓝牙读取数据失败(超时)")
                    msg.data = bundle
                    mHandler.sendMessage(msg)

                    val msg_fail = mHandler.obtainMessage(MainActivity.CONNECT_RESET)
                    mHandler.sendMessage(msg_fail)
                }

            }

            try {
                mmInStream!!.close()
                mmOutStream!!.close()
                mmSocket.close()

            } catch (e: IOException) {
                Log.e(TAG, "IOException:" + e)
            }

        }

        fun write(buffer: IntArray) {
            var bytes = 0
            try {
                while (bytes < buffer.size) {
                    mmOutStream!!.write(buffer[bytes])
                    bytes++
                }
                mmOutStream!!.flush()

            } catch (e: IOException) {
                Log.e(TAG, "Exception during write", e)
            }

        }

        fun write(out: Int) {
            try {
                mmOutStream!!.write(out)
                mmOutStream.flush()

            } catch (e: IOException) {
                Log.e(TAG, "Exception during write", e)
            }

        }

        fun cancel() {
            isExit = true
        }
    }

    companion object {

        private val TAG = "BtDevice"

        // Unique UUID for SerialComm UUID
        private val COM_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")

        val BLE_DATA_LENGTH = 20
    }

    // 蓝牙状态
    object BtState {
        const val STATE_NONE = 0
        const val STATE_LISTEN = 1
        const val STATE_CONNECTING = 2
        const val STATE_CONNECTED = 3
    }

}
